package ca.scotthyndman.as2haxe.dom;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.asdt.core.internal.antlr.AS2TokenTypes;

import ca.scotthyndman.as2haxe.dom.visitor.IVisitor;

/**
 * Represents a postfix expression.
 */
public class ASPostfixExpression extends ASExpression {

	/**
	 * The "operator" structural property of this node type.
	 * 
	 * @since 3.0
	 */
	public static final ASSimplePropertyDescriptor OPERATOR_PROPERTY = new ASSimplePropertyDescriptor(
			ASPostfixExpression.class,
			"operator", ASPostfixExpression.Operator.class, true); //$NON-NLS-1$

	/**
	 * The "operand" structural property of this node type.
	 * 
	 * @since 3.0
	 */
	public static final ASChildPropertyDescriptor OPERAND_PROPERTY = new ASChildPropertyDescriptor(
			ASPostfixExpression.class, "operand", ASExpression.class, true); //$NON-NLS-1$

	/**
	 * A list of property descriptors (element type:
	 * {@link ASPropertyDescriptor}), or null if uninitialized.
	 */
	private static final List<ASPropertyDescriptor> PROPERTY_DESCRIPTORS;

	static {
		List<Object> properyList = new ArrayList<Object>(3);
		createPropertyList(ASPostfixExpression.class, properyList);
		addProperty(OPERATOR_PROPERTY, properyList);
		addProperty(OPERAND_PROPERTY, properyList);
		PROPERTY_DESCRIPTORS = reapPropertyList(properyList);
	}

	/**
	 * Returns a list of structural property descriptors for this node type.
	 * Clients must not modify the result.
	 * 
	 * @return a list of property descriptors
	 */
	public static List<ASPropertyDescriptor> propertyDescriptors() {
		return PROPERTY_DESCRIPTORS;
	}

	/**
	 * A postfix operator.
	 */
	public enum Operator {
		INCREMENT("++", AS2TokenTypes.POST_INC),
		DECREMENT("--", AS2TokenTypes.POST_DEC);

		public static Map<Integer, Operator> typeToOperator;

		public final String token;

		public final int tokenType;

		Operator(String token, int tokenType) {
			this.token = token;
			this.tokenType = tokenType;

			init();
		}

		/**
		 * Initializes the operator
		 * 
		 */
		private void init() {
			if (typeToOperator == null) {
				typeToOperator = new HashMap<Integer, Operator>();
			}
			typeToOperator.put(this.tokenType, this);
		}

		/**
		 * Gets the operator associated with <code>tokenType</code>.
		 * 
		 * @param tokenType
		 *            The token type
		 * @return The operator
		 */
		public static Operator getOperator(int tokenType) {
			return typeToOperator.get(tokenType);
		}
	}

	private ASExpression operand;

	private Operator operator;

	/**
	 * Constructs a new instance of ASPostfixExpression
	 * 
	 * @param ast
	 *            The abstract syntax tree that owns this element
	 */
	public ASPostfixExpression(AST ast) {
		super(ast);
	}

	/**
	 * Sets the operand.
	 * 
	 * @param operand
	 *            the new operand
	 */
	public void setOperand(ASExpression operand) {
		if (operand == null) {
			throw new IllegalArgumentException();
		}
		// an Assignment may occur inside a Expression - must check cycles
		ASTNode oldChild = this.operand;
		preReplaceChild(oldChild, operand, OPERAND_PROPERTY);
		this.operand = operand;
		postReplaceChild(oldChild, operand, OPERAND_PROPERTY);
	}

	/**
	 * Returns the operand.
	 * 
	 * @return the operand
	 */
	public ASExpression getOperand() {
		return operand;
	}

	/**
	 * Sets the operator.
	 * 
	 * @param operator
	 */
	public void setOperator(Operator operator) {
		if (operator == null) {
			throw new IllegalArgumentException();
		}
		preValueChange(OPERATOR_PROPERTY);
		this.operator = operator;
		postValueChange(OPERATOR_PROPERTY);
	}

	/**
	 * Returns the operator
	 * 
	 * @return The operator
	 */
	public Operator getOperator() {
		return operator;
	}

	//
	// ======== INTERNAL
	//

	/*
	 * (omit javadoc for this method) Method declared on ASTNode.
	 */
	final Object internalGetSetObjectProperty(
			ASSimplePropertyDescriptor property, boolean get, Object value) {
		if (property == OPERATOR_PROPERTY) {
			if (get) {
				return getOperator();
			} else {
				setOperator((Operator) value);
				return null;
			}
		}
		// allow default implementation to flag the error
		return super.internalGetSetObjectProperty(property, get, value);
	}

	/*
	 * (omit javadoc for this method) Method declared on ASTNode.
	 */
	final ASTNode internalGetSetChildProperty(
			ASChildPropertyDescriptor property, boolean get, ASTNode child) {
		if (property == OPERAND_PROPERTY) {
			if (get) {
				return getOperand();
			} else {
				setOperand((ASExpression) child);
				return null;
			}
		}
		// allow default implementation to flag the error
		return super.internalGetSetChildProperty(property, get, child);
	}

	// ===== TO STRING AND VISITOR

	@Override
	public String toString() {
		return getOperand().toString() + getOperator().token;
	}

	@Override
	public void accept(IVisitor as2Visitor, boolean recursive) {
		recursive = as2Visitor.visit(this);

		if (!recursive) {
			return;
		}

		getOperand().accept(as2Visitor, recursive);
	}
}
